#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <pthread.h>
#include "mplayer.h"
#include "double_link.h"

#define PATTERN "/home/ll/Music/*.mp3"
#define FIFOPATH "music.fifo"
#define MUSICSIZE 256
#define OPTSIZE 512
#define NAMEBUFSIZE 256
#define ROW 15
#define LINE 10

/* 音乐名字数组*/
char musicBuff[MUSICSIZE] = {0};	
/* 链表头结点 */
LList_st *head = NULL;
/* 链表当前位置 */
llist *cur = NULL;
/* 进程号 */
pid_t pid;
/* golb参数 */
glob_t buff;
/* 播放模式 */
int playWay = ORDER;
/* 播放器状态 */
int tmp = 0;

/* 主界面 */
void interface()
{
	int i, j, k;

	printf("\033[2J\033[5;25H\033[44m--====================--------------------\033[0m\n");
	printf("%c[6;25H",'\033');
	
	for (i = 0; i < ROW; i++)
	{
		printf("\033[44m||\033[0m");
		for (j = 0; j < LINE; j++)
		{
			printf("  ");
		}
		printf("\033[44m||\t\t||\033[0m\n\033[24C");

	}
	
	printf("\033[6;35H%s\n","歌单");
	printf("\033[6;53H\033[42m%s\033[0m\n","按键说明");
	printf("\033[8;52H%s\n","播放    'b'");
	printf("\033[9;52H%s\n","暂停    'p'");
	printf("\033[10;52H%s\n","退出    'q'");
	printf("\033[12;52H%s\n","上一首  'a'");
	printf("\033[13;52H%s\n","下一首  'd'");
	printf("\033[15;52H%s\n","上一页  'w'");
	printf("\033[16;52H%s\n","下一页  's'");
	printf("\033[18;52H\033[42m%s\033[0m\n"," 播放模式 ");
	printf("\033[21;25H\033[44m--====================--------------------\033[0m\n");
}

/* 歌曲路径和名字导入链表 */
LList_st *loading()
{
	int i, ret;
	musicNode name;
	LList_st *head = NULL;

	head = llist_create(sizeof(name));
	if (head == NULL)
	{
		fprintf(stderr,"llist_create() faild\n");
		exit(1);
	}

	glob(PATTERN,0,NULL,&buff);
	for (i = 0; i < buff.gl_pathc; i++)
	{
		strncpy(name.musicName,buff.gl_pathv[i],NAMESIZE);
		ret = llist_insert(head,&name,BEHIND);
		if (ret < 0)
		{
			fprintf(stderr,"llist_insert() fail\n");		
			exit(1);
		}
	}
	return head;
}

/* 从链表中获取歌曲名字 */
char *getNameFromLink(void *record)
{
	musicNode *dest = record;
	return dest->musicName;
}

/* 获取不包含路径的名字 */
char *getRealName(char *name)
{
	char *pos = NULL;

	pos = strrchr(name,'/');
	if (pos == NULL)
		return name;
	return pos+1;
}

/* 模式显示 */
void showPatten()
{
	if (playWay == ORDER)
		printf("\033[19;52H%s\n"," 顺序播放 ");
	else if (playWay == SIGLOOP)
		printf("\033[19;52H%s\n"," 循环播放 ");
	else
		printf("\033[19;52H%s\n"," 随机播放 ");
}

/* 歌曲页数 */
int songPage()
{
	int i;
	int firstPage = 0; 

	for (i = 0; i < buff.gl_pathc; i++)
	{
		if (strcmp(buff.gl_pathv[i],getNameFromLink(cur->data)) == 0)
		{
			firstPage = i;
			break;	
		}
	}
	return firstPage;
}

/* 歌单的滑动显示,因为ansi控制码不好控制,根据需求自行添加 */
/*
void *curSongNameList(void *argc)
{
	int i, j, len;
	int count;
	char *buf = (char *)argc;
	
	len = strlen(buf);
	while (1)
	{
		for (j = 0; j < len; j += 3)
		{
			count = SIZE;
			printf("\033[10;40H");
			for (i = j; i < len && (i - j) < 3*N; i++)
			{
				printf("%c",buf[i]);
			}

			fflush(stdout);
			usleep(700000);	
			printf("\033[10;40H");
			while (count--)
				printf(" ");
			pthread_testcancel();
		}
	}
}
*/

/* 歌单展示
 *当当前链表歌曲和播放歌曲一样时，以红色背景色显示
 *
 */
void showList(llist *cur,enum song_way pageTmp)
{
	char *name = NULL;
	char nameBuf[NAMEBUFSIZE] = {0};
	int i = 0;
	int n = 0;
	int firstPage;

	firstPage = songPage(cur);

	i = 12 * (firstPage / 12);
	if (i % 12 == 0)
	{
		interface();
	}

	printf("\033[8;31H");
	if (pageTmp == NEXT)
	{
		if (i + 12 < buff.gl_pathc)
			i += 12;
	}
	else if (pageTmp == LAST)
	{
		if (i - 12 >= 0)
			i -= 12;
	}
	for (; i < buff.gl_pathc && n < 12; i++, n++)
	{
		name = getRealName(buff.gl_pathv[i]);
		if (strlen(name) < 18)
		{
			if (i == firstPage)
				printf("\033[41m%s\033[0m\n\033[30C",name);
			else		
				printf("%s\n\033[30C",name);
		}
		else
		{
			if (i == firstPage)
				printf("\033[41m%s\033[0m\n\033[30C",strncpy(nameBuf,name,9));
			else
				printf("%s\n\033[30C",strncpy(nameBuf,name,18));
		}	
	}
	showPatten();
	
}

/* 线程函数,根据播放模式不同来播放下一首 */
void *playPat(void *args)
{
	int n;

	if (playWay == ORDER)
	{
		cur = cur->next;
		if (cur == &head->head)
			cur = cur->next;
		strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
		showList(cur,0);
		fflush(stdout);
		pid = fork();
		createProgress(pid, musicBuff);
		
	}
	else if (playWay == SIGLOOP)
	{
		fflush(stdout);
		pid = fork();
		createProgress(pid, musicBuff);
	}
	else
	{
		srand((unsigned int)time(NULL));
		n = (1 + rand() % 10);
		while (n--)
		{		
			cur = cur->next;
		}
		if (cur == &head->head)
			cur = cur->next;
		strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
		showList(cur,0);
		fflush(stdout);
		pid = fork();
		createProgress(pid,musicBuff);
	}
	tmp = 1;
}

/* 信号函数，接到信号时判断子进程是否正常结束，如果正常结束就根据播放模式播放下一首 */
void actionHandler(int s)
{
	int status;
	pthread_t tid;

	wait(&status);
	if (WIFEXITED(status))
	{
		pthread_create(&tid, NULL, playPat, NULL);
		pthread_detach(tid);
		tmp = 0;
	}

}

/* 创建子进程播放歌曲并将3个标准流置null */
void createProgress(pid_t pid,char *musicBuff)
{
	int fd;
	if (pid < 0)
	{
		perror("fork()");
		exit(1);
	}
	if (pid == 0)
	{
		fd = open("/dev/null",O_RDWR);
		if (fd < 0)
		{
			perror("open()");
			exit(1);
		}
		dup2(fd,0);
		dup2(fd,1);
		dup2(fd,2);
		if (fd > 2)
			close(fd);

		execlp("mplayer", "mplayer", "-slave", "-quiet", "-input", "file=music.fifo", musicBuff, NULL);
		perror("execlp()");
		exit(1);
	}
}

/* 导入歌曲 */
void loadfile()
{
	int fd;
	char optName[OPTSIZE] = {0};

	fd = open(FIFOPATH, O_WRONLY);	
	if (fd < 0)
	{
		perror("open()");
		exit(1);
	}
	
	sprintf(optName, "%s %s\n", "loadfile", musicBuff);
	write(fd, optName, strlen(optName));
	close(fd);
}

/* 歌曲暂停 */
void songPause()
{
	int fd;

	fd = open(FIFOPATH, O_WRONLY);	
	if (fd < 0)
	{
		perror("open()");
		exit(1);
	}
	
	write(fd, "pause\n", strlen("pause\n"));
	close(fd);

}

/* 歌曲退出 */
void songStop()
{
	int fd;

	fd = open(FIFOPATH, O_WRONLY);	
	if (fd < 0)
	{
		perror("open()");
		exit(1);
	}
	
	write(fd, "quit\n", strlen("quit\n"));
	close(fd);
}

/* 运行函数
 * 关闭系统回显和getchar()以\n结尾的设置
 *
 * flag播放状态 0:未播放 1:正在播放 2:暂停 
 * tmp检测第一次是否创建了子进程，防止意外杀死主进程 0:未创建子进程 1:已创建子进程
 *
 */
void operationFunction()
{
	struct termios new,old;
	struct sigaction sa;
	char ch;

	tcgetattr(0,&old);
	tcgetattr(0,&new);

	new.c_lflag = new.c_lflag & ~(ICANON | ECHO);
	new.c_cc[VMIN] = 1;
	new.c_cc[VTIME] = 0;

	tcsetattr(0,TCSANOW,&new);
	printf("\033[?25l");	//隐藏光标
	interface();


	unlink(FIFOPATH);
	if (mkfifo(FIFOPATH, 0666) < 0)
	{
		perror("mkfifo()");
		exit(1);
	}

	sa.sa_handler = actionHandler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sigaction(SIGCHLD, &sa, NULL);

	head = loading();
	cur = head->head.next;
	showList(cur,0);
	while(1)
	{
		ch = getchar();
		switch (ch)
		{
			/* 播放 */
			case 'b':
				if (tmp == 0)
				{
					if (cur == NULL)
						cur = cur->next;
					strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
					showList(cur,0);
					fflush(stdout);
					pid = fork();
					tmp = 1;
					createProgress(pid,musicBuff);
				}
				break;
			/* 上一页 */
			case 'w':
				interface();
				showList(cur,LAST);
				break;
			/* 下一页 */
			case 's':
				interface();
				showList(cur,NEXT);
				break;
			/* 上一首 */
			case 'a':
				cur = cur->prev;
				if (cur == &head->head)
					cur = cur->prev;
			strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
			showList(cur,0);
			if (tmp == 1)
				loadfile();
			break;
			/* 下一首 */
			case 'd':
				cur = cur->next;
				if (cur == &head->head)
					cur = cur->next;
			strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
			showList(cur, 0);
			if (tmp == 1)
				loadfile();
			break;
			/* 暂停 | 恢复播放 */
			case 'p':
			if (tmp == 1)
				songPause();
			break;
			case 't':
				playWay++;
				if (playWay > 3)
					playWay = 1;
				showPatten();
			break;
			default:
			break;
		}
		/* 退出 */
		if (ch == 'q')
		{
			if (tmp)
			{
				songStop();
				kill(pid, SIGKILL);
			}
			break;
		}
	}
	tcsetattr(0,TCSANOW,&old);	//还原终端原始设置
	printf("\033[?25h\033[2J");
}
